home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
033a
/
cwexp104.zip
/
EXPIRE.C
next >
Wrap
C/C++ Source or Header
|
1991-10-22
|
23KB
|
723 lines
/*
expire - A Waffle utility to provide news expiration based on age of
articles. It can also be used to expire files in directories
related (or unrelated) to Waffle.
Original author: Chris Winemiller, cwinemil@keys.lonestar.org
History:
02 Apr 1991 1.0 Chris Winemiller. Original version.
09 Jul 1991 1.01 Chris Winemiller. Corrected the "usage" info
printed out when one enters "expire -h".
Previously, the usage explained the -t option but
failed to place it in the invocation info. (I.e.,
previously said "expire [-a -n]" rather than
"expire [-a -n -t -h]".) Oh--I also added the -h
option to this list. Actually, any option other
than -a, -n, or -t will cause the usage to print
out. Expanded the help output to mention the
/mexp attribute.
20 Sep 1991 1.02 Chris Winemiller. Added "-e <expdirs_file>"
option to name an "expdirs" file. This file will
contain the name of directories whose files
should be subjected to expiration. Thanks to
Bob Kirkpatrick (bobk@dogear.spk.wa.us) for
suggesting the existence and format of this file.
21 Oct 1991 1.03 Modified so that expire prints out the total
number of files and total number of bytes deleted.
These statistics are printed just before expire
terminates.
22 Oct 1991 1.04 Added a couple more statistics printed out at the
end: files per second and bytes per second that
were deleted (and total time, too).
Invocation: expire [-a -e <expire_file> -h -n -t]
where:
-a = Consider all files in each news group directory or for
expire directory for possible deletion. (Default:
consider only files whose names are composed of only
numerical characters (0-9) and no filename extension.)
-e = Name of an "expire" file. This file contains the names
of directories whose files should be subjected to
expiration. (Multiple -e options are permitted.)
-h = Help. Produces the "usage" printout.
-n = No file deletions are performed, but otherwise
produces the same output. (I.e., -n will report the
files that should be deleted, but doesn't delete
them.)
-t = Display the expiration time for each news group.
Any other attempted option produces a usage description.
Compilation command: (Turbo C++ compiler): tcc -mt -lt expire.c getopt.c
where -mt specifies tiny model for compilation and
-lt tells the linker to produce a .COM executable.
*/
/* Page */
/*
Include files
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dir.h>
#include <dos.h>
#include <time.h>
/* Page */
/*
Type definitions, etc.
*/
#define VERSION "1.04 by C.L.W."
#define DEFAULT_TIME 72L /* Default expiration time of 3 days */
#define MAXPATHSIZE 128 /* Max length of any file pathname */
#define MAXLINELENGTH 256 /* Max length of a line in a file */
typedef struct entry
{
struct entry *next;
char *filename;
} pathEntry;
short prohibitDeletions = 0; /* Turned on with -n command line option */
short checkAllFiles = 0; /* Turned on with -a command line option */
short displayExpiration = 0; /* Turned on with -t command line option */
long totalFilesDeleted = 0L; /* Number of files deleted */
long totalBytesDeleted = 0L; /* Number of bytes deleted */
/* Page */
/*
Functions defined in other files.
*/
int getopt(int nargc, char **nargv, char *ostr);
extern char *optarg;
extern int optind;
/*
Functions in this file.
*/
int main( int argc, char *argv[] );
pathEntry *theForumFilenames( char *staticFilename );
void addToExpdirsFilenames( char *expdirsFilename, pathEntry **expdirList );
void removeOldNews( char *forumFilename );
void removeOldFiles( char *expdirsFilename );
void getTokenValue( char *token, char *value, int *processed );
void deleteOldFiles( char *directory, time_t age, pathEntry *exclusions );
pathEntry *makeFilenameList( char *filenameString );
void killFilenameList( pathEntry **list );
void copyFilenameList( pathEntry *srclist, pathEntry **dstlist );
int fileIsNotExcluded( pathEntry *list, char *name );
void usage( void );
/* Page */
int main( int argc, char *argv[] )
{
int optionCharacter;
pathEntry *forums = theForumFilenames( getenv( "WAFFLE" ) );
pathEntry *expdirsList = (pathEntry *)0;
time_t startTime = time( 0 );
time_t elapsedTime;
while ( ( optionCharacter = getopt( argc, argv, "ae:hnt" ) ) != EOF )
{
switch ( optionCharacter )
{
case 'a':
checkAllFiles = 1;
break;
case 'n':
prohibitDeletions = 1;
break;
case 't':
displayExpiration = 1;
break;
case 'e':
addToExpdirsFilenames( optarg, &expdirsList );
break;
case 'h':
default:
usage();
exit(1);
break;
}
}
while ( forums )
{
removeOldNews( forums->filename );
forums = forums->next;
}
while ( expdirsList )
{
removeOldFiles( expdirsList->filename );
expdirsList = expdirsList->next;
}
elapsedTime = time( 0 ) - startTime;
if ( elapsedTime <= 0L ) elapsedTime = 1L;
printf( "\n\n%ld file%s (%ld byte%s) %sdeleted at a rate of:",
totalFilesDeleted, ( totalFilesDeleted == 1L ) ? "" : "s",
totalBytesDeleted, ( totalBytesDeleted == 1L ) ? "" : "s",
prohibitDeletions ? "would have been " : "" );
printf( "\n%ld files/sec (%ld bytes/sec)",
totalFilesDeleted / elapsedTime,
totalBytesDeleted / elapsedTime );
printf( "\nElapsed time %.2ld:%.2ld:%.2ld",
elapsedTime / 3600,
elapsedTime / 60,
elapsedTime % 60 );
printf( "\n\nExpire version %s\n", VERSION );
return 0;
}
/* Page */
/*
Function: theForumFilenames
Purpose: Given the name of the Waffle "Static" file, create a
linked list of filenames and return a pointer to the
first link entry. Each filename is the name of a Waffle
"Forum" type file. A forum file contains the
specifications for news groups (local or external) which
must be checked for news expiration.
*/
pathEntry *theForumFilenames( char *waffleStaticFilename )
{
pathEntry *listHead = 0;
pathEntry *listEnd = 0;
char *keywordSeparator;
char waffleRootDirectory[ MAXPATHSIZE ];
char forumFilename[ MAXPATHSIZE ];
char line[ MAXLINELENGTH ];
char keyword[ MAXLINELENGTH ];
FILE *waffleFp;
int result;
if ( ( waffleFp = fopen( waffleStaticFilename, "r" ) ) == NULL )
{
perror( waffleStaticFilename );
}
else
{
/* Scan the static file for forum filenames. */
while ( fgets( line, MAXLINELENGTH, waffleFp ) )
{
result = sscanf( line, " %s", keyword );
if ( result != 1 ) continue; /* No keyword on this line */
if ( strncmpi( keyword, "waffle", 6 ) == 0 )
{ /* Found keyword for waffle's root directory */
if ( ( keywordSeparator = strchr( line, ':' ) ) != 0 )
{
sscanf( ++keywordSeparator, " %s", waffleRootDirectory );
}
}
else if (strncmpi( keyword, "forums", 6 ) == 0 )
{ /* Found keyword which will name forum definition file(s) */
if ( ( keywordSeparator = strchr( line, ':' ) ) != 0 )
{
++keywordSeparator;
do
{
while ( (*keywordSeparator == ' ') ||
(*keywordSeparator == '\t') ) ++keywordSeparator;
forumFilename[ 0 ] = '\0';
result = sscanf( keywordSeparator, "%s", forumFilename );
if ( result == 1 )
{
if ( listHead == 0 )
{
listHead = (pathEntry *) malloc(sizeof(pathEntry));
listEnd = listHead;
}
else
{
listEnd->next = (pathEntry *)malloc(sizeof(pathEntry));
if ( listEnd->next ) listEnd = listEnd->next;
}
listEnd->next = 0;
listEnd->filename = malloc( MAXPATHSIZE );
strcpy( listEnd->filename, forumFilename );
keywordSeparator += strlen( forumFilename );
}
}
while ( result == 1 );
}
}
}
fclose( waffleFp );
}
for ( listEnd = listHead; listEnd; listEnd = listEnd->next )
{
strcpy ( forumFilename, listEnd->filename );
strcpy ( listEnd->filename, waffleRootDirectory );
strcat ( listEnd->filename, "/system/" );
strcat ( listEnd->filename, forumFilename );
}
return listHead;
}
/* Page */
/*
Function: addToExpdirsFilenames
Purpose: Add a new file name to a list of file names. This list is
expected to be a list of "expdirs" files, each of which
names directories whose files should also be subjected to
expiration.
*/
void addToExpdirsFilenames( char *expdirsFilename, pathEntry **expdirList )
{
register pathEntry *aPath;
register pathEntry *newEntry;
newEntry = (pathEntry *)malloc( sizeof( pathEntry ) );
newEntry->next = (pathEntry *)0;
newEntry->filename = malloc( strlen( expdirsFilename ) + 1 );
strcpy( newEntry->filename, expdirsFilename );
if ( ( aPath = *expdirList ) != ( pathEntry *)0 )
{
while ( aPath->next ) aPath = aPath->next;
aPath->next = newEntry;
}
else *expdirList = newEntry;
}
/* Page */
/*
Function: removeOldNews
Purpose: Given the name of the forum file, remove news articles
according to the proper expiration time.
*/
void removeOldNews( char *forumFilename )
{
time_t defaultExpire = DEFAULT_TIME;
time_t currentExpire = defaultExpire;
pathEntry *currentExcludes = (pathEntry *)0;
int length;
int is_newsgroup;
FILE *forumFp;
char *token;
char parameterName[ 24 ];
char parameterValue[ MAXPATHSIZE ];
char line[ MAXLINELENGTH ];
char newsGroup[ MAXLINELENGTH ];
char newsRootDirectory[ MAXPATHSIZE ];
char newsGroupDirectory[ MAXPATHSIZE ];
newsGroup[ 0 ] = '\0';
newsRootDirectory[ 0 ] = '\0';
newsGroupDirectory[ 0 ] = '\0';
if ( ( forumFp = fopen( forumFilename, "r" ) ) == 0 )
{
putchar( '\n' );
perror( forumFilename );
}
else
{
while ( fgets( line, MAXLINELENGTH, forumFp ) )
{
if ( *line == '#' ) continue; /* Comment line; skip */
line[ strlen( line ) - 1 ] = '\0'; /* Remove '\n' at end */
newsGroupDirectory[ 0 ] = '\0'; /* Remove '\n' at end */
token = line + strspn( line, " \t" );
if ( ( length = strcspn( token, " \t" ) ) == 0 ) continue;
strncpy( newsGroup, token, length );
newsGroup[ length ] = '\0';
is_newsgroup = ( stricmp( newsGroup, "DEFAULT" ) != 0 ) &&
( stricmp( newsGroup, "FORUM" ) != 0 );
while ( token += length,
length = strspn( token, " \t"),
token += length,
( length = strcspn( token, " \t=" ) ) != 0 )
{
strncpy( parameterName, token, length );
parameterName[ length ] = '\0';
if ( token[ length ] == '=' )
{
token += length + 1;
getTokenValue( token, parameterValue, &length );
}
if ( stricmp( parameterName, "/dir" ) == 0 )
{
strcpy( is_newsgroup ? newsGroupDirectory
: newsRootDirectory, parameterValue );
}
else if ( stricmp( parameterName, "/mexp" ) == 0 )
{
currentExpire = atol( parameterValue );
if ( is_newsgroup == 0 ) defaultExpire = currentExpire;
}
}
if ( is_newsgroup )
{
printf( "\n%s", newsGroup );
if ( displayExpiration )
{
printf( " (%ld hour expiration)", currentExpire);
}
if ( *newsGroupDirectory == '\0' )
{
while ( ( token = strchr( newsGroup, '.' ) ) != 0 ) *token = '/';
strcpy( newsGroupDirectory, newsRootDirectory );
strcat( newsGroupDirectory, "/" );
strcat( newsGroupDirectory, newsGroup );
}
deleteOldFiles( newsGroupDirectory, currentExpire,
currentExcludes );
currentExpire = defaultExpire;
}
}
fclose( forumFp );
}
}
/* Page */
/*
Function: removeOldFiles
Purpose: Given the name of an "expdirs" file, remove files from
the directories named therein, according to the proper
expiration time.
*/
void removeOldFiles( char *expdirsFilename )
{
time_t defaultExpire = DEFAULT_TIME;
time_t currentExpire = defaultExpire;
pathEntry *currentExcludes = (pathEntry *)0;
pathEntry *defaultExcludes = (pathEntry *)0;
int length;
int is_directory;
FILE *expFp;
char *token;
char parameterName[ 24 ];
char parameterValue[ MAXPATHSIZE ];
char line[ MAXLINELENGTH ];
char directory[ MAXLINELENGTH ];
if ( ( expFp = fopen( expdirsFilename, "r" ) ) == 0 )
{
putchar( '\n' );
perror( expdirsFilename );
}
else
{
while ( fgets( line, MAXLINELENGTH, expFp ) )
{
if ( *line == '#' ) continue; /* Comment line; skip */
line[ strlen( line ) - 1 ] = '\0'; /* Remove '\n' at end */
directory[ 0 ] = '\0';
token = line + strspn( line, " \t" );
if ( ( length = strcspn( token, " \t" ) ) == 0 ) continue;
strncpy( directory, token, length );
directory[ length ] = '\0';
is_directory = ( stricmp( directory, "DEFAULT" ) != 0 );
while ( token += length,
length = strspn( token, " \t" ),
token += length,
( length = strcspn( token, " \t=" ) ) != 0 )
{
strncpy( parameterName, token, length );
parameterName[ length ] = '\0';
if ( token[ length ] == '=' )
{
token += length + 1;
getTokenValue( token, parameterValue, &length );
}
if ( stricmp( parameterName, "/mexp" ) == 0 )
{
currentExpire = atol( parameterValue );
if ( is_directory == 0 ) defaultExpire = currentExpire;
}
else if ( stricmp( parameterName, "/exclude" ) == 0 )
{
killFilenameList( ¤tExcludes );
currentExcludes = makeFilenameList( parameterValue );
if ( is_directory == 0 )
{
killFilenameList( &defaultExcludes );
copyFilenameList( currentExcludes, &defaultExcludes );
}
}
}
if ( is_directory )
{
printf( "\n%s", directory );
if ( displayExpiration )
{
printf( " (%ld hour expiration)", currentExpire);
}
deleteOldFiles( directory, currentExpire, currentExcludes );
currentExpire = defaultExpire;
killFilenameList( ¤tExcludes );
copyFilenameList( defaultExcludes, ¤tExcludes );
}
}
fclose( expFp );
}
}
/* Page */
/*
Function: getTokenValue
Purpose: Identify the non-blank characters constituting the
value of the specified token.
*/
void getTokenValue( char *token, char *value, int *processed )
{
int length;
if ( *token == '"' )
{
length = strcspn( ++token, "\"" );
strncpy( value, token, length );
value[ length ] = '\0';
length += 2;
}
else
{
length = strcspn( token, " \t" );
strncpy( value, token, length );
value[ length ] = '\0';
}
*processed = length;
}
/* Page */
/*
Function: deleteOldFiles
Purpose: Delete files in the specified directory which are
at least as old as the specified age.
*/
void deleteOldFiles( char *directory, time_t age, pathEntry *exclusions )
{
# define OUTPUTLINELENGTH 80
struct ffblk findblock;
char *filenameLocation;
time_t theTime = time( 0 );
time_t fileDate;
struct date d_date;
struct time d_time;
int done;
short fileCount;
short currentCount;
age *= 3600L; /* Convert age from hours to seconds */
filenameLocation = directory + strlen( directory );
strcpy( filenameLocation++, "/*.*" );
done = findfirst( directory, &findblock, 0 );
fileCount = OUTPUTLINELENGTH / ( strlen( findblock.ff_name ) + 1 ) - 1;
currentCount = 0;
while ( ! done )
{
d_date.da_year = ( (findblock.ff_fdate & 0xfe00) >> 9) + 1980;
d_date.da_mon = (findblock.ff_fdate & 0x01e0) >> 5;
d_date.da_day = (findblock.ff_fdate & 0x1f);
d_time.ti_hour = (findblock.ff_ftime & 0xf800) >> 11;
d_time.ti_min = (findblock.ff_ftime & 0x07e0) >> 5;
d_time.ti_sec = (findblock.ff_ftime & 0x1f) * 2;
d_time.ti_hund = 0;
fileDate = dostounix( &d_date, &d_time );
if ( ( ( theTime - fileDate ) >= age ) &&
( ( checkAllFiles ) ||
( strspn( findblock.ff_name, "0123456789" ) ==
strlen(findblock.ff_name ) ) ) &&
( fileIsNotExcluded( exclusions, findblock.ff_name ) ) )
{
if ( --currentCount <= 0 )
{
currentCount = fileCount;
putchar( '\n' );
}
strcpy( filenameLocation, findblock.ff_name );
if ( prohibitDeletions == 0 ) unlink( directory );
totalFilesDeleted++;
totalBytesDeleted += findblock.ff_fsize;
putchar( ' ' );
fputs( findblock.ff_name, stdout );
}
done = findnext( &findblock );
}
}
/* Page */
/*
Function: makeFilenameList
Purpose: Make a linked list of pathEntry structures, each holding the
name of a file. The input consists of a single character
string containing file names separated by commas.
*/
pathEntry *makeFilenameList( char *filenameString )
{
register pathEntry *tmp;
int length = 0;
pathEntry *theList = (pathEntry *)0;
while ( filenameString += length,
length = strspn( filenameString, ", \t" ),
filenameString += length,
( length = strcspn( filenameString, ", \t" ) ) != 0 )
{
tmp = (pathEntry *)malloc( sizeof( pathEntry ) );
tmp->filename = (char *)malloc( length + 1 );
strncpy( tmp->filename, filenameString, length );
tmp->filename[ length ] = '\0';
tmp->next = theList;
theList = tmp;
}
return theList;
}
/* Page */
/*
Function: killFilenameList
Purpose: Delete all entries in list, and make list a null pointer.
*/
void killFilenameList( pathEntry **list )
{
register pathEntry *tmp = *list;
register pathEntry *next;
while ( tmp )
{
next = tmp->next;
free( (void *)tmp->filename );
free( (void *)tmp );
tmp = next;
}
*list = (pathEntry *)0;
}
/* Page */
/*
Function: copyFilenameList
Purpose: create a new list (dstlist) from an existing list (srclist).
Ignore previous contents of dstlist.
*/
void copyFilenameList( pathEntry *srclist, pathEntry **dstlist )
{
register pathEntry *tmp, *prevtmp;
for ( *dstlist = prevtmp = (pathEntry *)0;
srclist;
srclist = srclist->next, prevtmp = tmp )
{
tmp = (pathEntry *)malloc( sizeof( pathEntry ) );
tmp->filename = (char *)malloc( strlen( srclist->filename ) + 1 );
strcpy( tmp->filename, srclist->filename );
tmp->next = (pathEntry *)0;
if ( prevtmp ) prevtmp->next = tmp;
else *dstlist = tmp;
}
}
/* Page */
/*
Function: fileIsNotExcluded
Purpose: Return true if name is not in list, false otherwise.
*/
int fileIsNotExcluded( register pathEntry *list, char *name )
{
while ( list && ( stricmp( list->filename, name ) != 0 ) )
{
list = list->next;
}
return list == (pathEntry *)0;
}
/* Page */
/*
Function: usage
Purpose: Print out the usage instructions for expire.
*/
void usage( void )
{
puts( "Usage: expire [-a -e <expire_file> -h -n -t] where:" );
puts( "\t-a = Consider all files in each news group or expire directory." );
puts( "\t (Default: consider only files whose names consist" );
puts( "\t only of numerical characters (0-9) and no extension.)" );
puts( "\t-e = Name of an expire file naming directories to be expired." );
puts( "\t-h = help (i.e., this printout)");
puts( "\t-n = Reports, but doesn't delete, expired articles." );
puts( "\t-t = Also display the expiration time for each news group." );
printf( "\nExpire version %s\n", VERSION );
}